﻿/*
   Copyright 2011 Softisis

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

using System.Collections.Generic;
using FOM.Impl;
using FOM.Impl.CompactExtensions;
using FOM.Impl.Converters;
using FOM.Interfaces;
using NUnit.Framework;

namespace TestUnits
{
    /// <summary>
    /// All tests dealing with arrays
    /// </summary>
    /// <remarks>Copyright Softisis 2011 (France)</remarks>
    [TestFixture]
    public class ArrayMappingsTests
    {


        [SetUp]
        public void SetUp()
        {
        }

        /// <summary>
        /// map an array like any other oject
        /// </summary>
        [Test]
        public void TestSimpleArrayMapping()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            IMappingNode<ObjectA, ObjectB> rootmap = mngr.RegisterMapping<ObjectA, ObjectB>("test");
            rootmap.Map(a => a.UneListeDeB, b => b.BUnObjetA.UneListeDeB);

            // source object
            var srcA = TestObjectsHolder.GetObjectA();
            var srcDest = TestObjectsHolder.GetObjectB();
            srcDest.BUnObjetA=new ObjectA();
            srcDest.BUnObjetA.UneListeDeB=new List<ObjectB>();

            // to test that this array will be replaced by source array object
            srcDest.BUnObjetA.UneListeDeB.Add(new ObjectB());
            srcDest.BUnObjetA.UneListeDeB.Add(new ObjectB());

            ObjectB res = (ObjectB)mngr.ApplyMapping("test", srcA, srcDest);
            Assert.IsTrue(res != null);
            Assert.IsTrue(res.BUnObjetA != null);
            Assert.IsTrue(res.BUnObjetA.UneListeDeB != null);
            Assert.IsTrue(res.BUnObjetA.UneListeDeB.Count==1);
        }


        /// <summary>
        /// map an array content to another array content
        /// </summary>
        [Test]
        public void TestSimpleArrayContentMapping()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            IMappingNode<ObjectA, ObjectB> rootmap = mngr.RegisterMapping<ObjectA, ObjectB>("test");
            rootmap.GoTo(a => a.UneListeDeB, b => b.BUnObjetA.UneListeDeB)
                .Map(s => s, d => d)
                .UseConverter(new ListCrossConv());

            // source object
            var srcA = TestObjectsHolder.GetObjectA();
            var srcDest = TestObjectsHolder.GetObjectB();
            srcDest.BUnObjetA = new ObjectA();
            srcDest.BUnObjetA.UneListeDeB = new List<ObjectB>();
            // to test that this array will be merged by source object
            srcDest.BUnObjetA.UneListeDeB.Add(new ObjectB());
            srcDest.BUnObjetA.UneListeDeB.Add(new ObjectB());

            ObjectB res = (ObjectB)mngr.ApplyMapping("test", srcA, srcDest);
            Assert.IsTrue(res != null);
            Assert.IsTrue(res.BUnObjetA != null);
            Assert.IsTrue(res.BUnObjetA.UneListeDeB != null);
            Assert.IsTrue(res.BUnObjetA.UneListeDeB.Count == 3);
        }


        /// <summary>
        /// map an array content with on the fly creation
        /// </summary>
        [Test]
        public void TestSimpleArrayContentMappingCreation()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            IMappingNode<ObjectA, ObjectB> rootmap = mngr.RegisterMapping<ObjectA, ObjectB>("test");
            rootmap.GoTo(a => a.UneListeDeB, b => b.BUnObjetA.UneListeDeB)
                .Map(s => s, d => d).Map(s => s, d => d)//.OverrideTargetType<List<ObjectB>>() // map obj to obj
                .UseConverter(new ListCrossConv()); // use list converter to manipulate content

            // source object
            var srcA = TestObjectsHolder.GetObjectA();

            ObjectB res = (ObjectB)mngr.ApplyMapping("test", srcA);
            Assert.IsTrue(res != null);
            Assert.IsTrue(res.BUnObjetA != null);
            Assert.IsTrue(res.BUnObjetA.UneListeDeB != null);
            Assert.IsTrue(res.BUnObjetA.UneListeDeB.Count == 1);
        }

        /// <summary>
        /// Map an array by using a converter to change the target list type,
        /// and submap elements
        /// </summary>
        [Test]
        public void TestArrayMappingWithSubMappings()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            // submapping
            var subMaps = mngr.RegisterMapping<ObjectB>().NewInstance();
            subMaps.Map(s => s.BChaine); // copy only a string for each subobject

            IMappingNode<ObjectA, ObjectA> rootmap = mngr.RegisterMapping<ObjectA>("test");
            rootmap.Map(s => s.UneListeDeB).UseConverter(new ListCrossConv(subMaps)).NewInstance();

            var osrc = TestObjectsHolder.GetObjectA();
            osrc.UneListeDeB[0].BChaine = "hello world"; // is (sub)mapped
            osrc.UneListeDeB[0].BEntier= 123456; // no (sub)mapped

            var res = (ObjectA)mngr.ApplyMapping("test", osrc);
            Assert.IsFalse(res==null);
            Assert.IsFalse(res.UneListeDeB == null);
            Assert.IsTrue(res.UneListeDeB.Count == 1);
            Assert.IsTrue(res.UneListeDeB[0].BChaine == "hello world");
            Assert.IsFalse(res.UneListeDeB[0].BEntier == 123456);
        }


        /// <summary>
        /// Test submapping on a root object which is a list
        /// </summary>
        [Test]
        public void DirectRootSubMapping()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            // submapping
            var submap = mngr.RM<ObjectB>()
                .NewInstance();
            submap.MP(s => s.BCar); // map onbly bcar

            
            // dont do that, because submap would be hold the bcar property (sub)mapping, not on the ObejctB.
            // var submap = mngr.RegisterMapping<ObjectB>()
            //    .NewInstance.Map(s => s.BCar);



            mngr.RM<List<ObjectB>>("test").NI().CV(new ListCrossConv(submap));


            var b1 = new ObjectB();
            b1.BEntier = 666; // no map
            b1.BCar = 'X'; // mapped

            var b2 = new ObjectB();
            b2.BEntier = 777; // not mapped
            b2.BCar = 'Y'; // mapped

            var source = new List<ObjectB>() {b1, b2};
            var res=(List<ObjectB>)mngr.AM("test", source);

            Assert.IsNotNull(res);
            Assert.IsTrue(res.Count==2);
            Assert.IsFalse(object.ReferenceEquals(source[0], res[0]));
            Assert.IsFalse(object.ReferenceEquals(source[1], res[1]));

            Assert.IsTrue(res[0].BCar == 'X');
            Assert.IsTrue(res[1].BCar == 'Y');

            Assert.IsFalse(res[0].BEntier== 666);
            Assert.IsFalse(res[1].BEntier == 777);
        }

        /// <summary>
        /// Test submapping on a root object which is a list
        /// </summary>
        [Test]
        public void ReverseMap()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            var source = new S();
            source.fixArray = new R[3];

            var r0 = new R();
            r0.name = "r0";

            var r1 = new R();
            r1.name = "r1";

            var r2 = new R();
            r2.name = "r2";

            source.fixArray[0] = r0;
            source.fixArray[1] = r1;
            source.fixArray[2] = r2;


            var sm = mngr.RegisterMapping<S>("test");
            sm.Map(s => s.fixArray)
                .UseConverter(new ListCrossConv())
                .NewInstance(true);

            var rev = sm.Reverse();
            mngr.RegisterMapping(rev, "revmap");


            var ret = (S)mngr.ApplyMapping("test", source);
            Assert.IsFalse(object.ReferenceEquals(source.fixArray, ret.fixArray));


            var ret2 = (S)mngr.ApplyMapping("revmap", ret);
            Assert.IsFalse(object.ReferenceEquals(source.fixArray, ret.fixArray));


        }


        /// <summary>
        /// Show the differences between the new instance on the root object of a submap.
        /// By default, the object in the array is kept.
        /// </summary>
        [Test]
        public void TestSubMapWithAndWithoutNewInstanceOnRoot()
        {
            // create manager
            IManager mngr = new ManagerImpl();
            
            // submap with no new instance on sub object
            var sm = mngr.RegisterMapping<ObjectA>();
            
            // root map
            mngr.RegisterMapping<List<ObjectA>>("testWITHOUTNewOnSubObject")
                .UseConverter(new ListCrossConv(sm));

            // THE source test object tree (for both case 1 and 2)
            var a1 = new ObjectA();
            a1.Chaine = "A1";
            var a2 = new ObjectA();
            a2.Chaine = "A2";
            var source = new List<ObjectA> {a1, a2};

            var res = (List<ObjectA>)mngr.ApplyMapping("testWITHOUTNewOnSubObject", source);

            Assert.IsNotNull(res);
            Assert.IsTrue(res.Count==2);
            Assert.IsTrue(res[0].Chaine == "A1");
            Assert.IsTrue(res[1].Chaine == "A2");
            Assert.IsTrue(object.ReferenceEquals(res[0], a1)); // source object are keps (no nexw instance)
            Assert.IsTrue(object.ReferenceEquals(res[1], a2)); // source object are keps (no nexw instance)

            /**** with new instance ********/

            // submap with new instance on sub object
            var sm2 = mngr.RegisterMapping<ObjectA>().NewInstance();

            // root map
            mngr.RegisterMapping<List<ObjectA>>("testWITHNewOnSubObject")
                .UseConverter(new ListCrossConv(sm2));

            var res2 = (List<ObjectA>)mngr.ApplyMapping("testWITHNewOnSubObject", source);
            Assert.IsNotNull(res2);
            Assert.IsTrue(res2.Count == 2);
            Assert.IsFalse(res2[0].Chaine == "A1");
            Assert.IsFalse(res2[1].Chaine == "A2");
            Assert.IsFalse(object.ReferenceEquals(res2[0], a1)); // source object are new instancied
            Assert.IsFalse(object.ReferenceEquals(res2[1], a2)); // source object are new instancied
            Assert.IsFalse(object.ReferenceEquals(res2[0], a2)); // cross-test to be sure
            Assert.IsFalse(object.ReferenceEquals(res2[1], a1)); // cross-test to be sure
        }

        [Test]
        public void TestFixArray()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            var source = new S();
            source.fixArray=new R[3];

            var r0 = new R();
            r0.name = "r0";

            var r1 = new R();
            r1.name = "r1";

            var r2 = new R();
            r2.name = "r2";

            source.fixArray[0] = r0;
            source.fixArray[1] = r1;
            source.fixArray[2] = r2;


            var sm = mngr.RegisterMapping<S>("test");
            sm.Map(s => s.fixArray)
                .UseConverter(new ListCrossConv())
                .NewInstance(true);

            var ret=(S)mngr.ApplyMapping("test", source);

            Assert.IsFalse(object.ReferenceEquals(source.fixArray, ret.fixArray));
        }
    }
}
